package com.yntsoft.mybatisUtil;


import java.sql.Connection;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;


/*args = {}用来表示拦截那个类得那个方法，拦截器可用于Executor,ParameterHandler,ResultSetHandler和StatementHandler这些类上。*/ 

//拦截  StatementHandler中的  包含  Connection对象的  prepare 方法
@Intercepts({ @org.apache.ibatis.plugin.Signature(method = "prepare", type = StatementHandler.class, args = {
        Connection.class})})
public class PaginationInterceptor extends BasePagination implements Interceptor {
    
    //intercept方法就是要进行拦截的时候要执行的方法
    public Object intercept(Invocation invocation)
        throws Throwable {
        
        
        
        RoutingStatementHandler handler = (RoutingStatementHandler)invocation.getTarget();

        StatementHandler delegate = (StatementHandler)ReflectUtil.getFieldValue(handler, "delegate");

        BoundSql boundSql = delegate.getBoundSql();

        Object obj = boundSql.getParameterObject();

        if ((obj instanceof Page)) {
            Page page = (Page)obj;

            MappedStatement mappedStatement = (MappedStatement)ReflectUtil.getFieldValue(delegate,"mappedStatement");

            Connection connection = (Connection)invocation.getArgs()[0];

            String sql = boundSql.getSql();

            setTotalRecord(page, mappedStatement, connection);

            String pageSql = getPageSql(page, sql);

            dispatch(page, mappedStatement, boundSql);
            ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
        }
        return invocation.proceed();
    }

    protected void dispatch(Page<?> page, MappedStatement mappedStatement, BoundSql boundSql) {}

    //plugin方法是拦截器用于封装目标对象的，通过该方法我们可以返回目标对象本身，也可以返回一个它的代理
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    /*setProperties方法是用于在Mybatis配置文件中指定一些属性的。定义自己的Interceptor最重要的是要实现plugin方法和intercept方法，
            在plugin方法中我们可以决定是否要进行拦截进而决定要返回一个什么样的目标对象。*/
    /*void setProperties(Properties properties);*/
    
    /**
     * 
     //对于StatementHandler其实只有两个实现类，一个是RoutingStatementHandler，另一个是抽象类BaseStatementHandler，
        //BaseStatementHandler有三个子类，分别是SimpleStatementHandler，PreparedStatementHandler和CallableStatementHandler，
        //SimpleStatementHandler是用于处理Statement的，PreparedStatementHandler是处理PreparedStatement的，而CallableStatementHandler是
        //处理CallableStatement的。Mybatis在进行Sql语句处理的时候都是建立的RoutingStatementHandler，而在RoutingStatementHandler里面拥有一个
        //StatementHandler类型的delegate属性，RoutingStatementHandler会依据Statement的不同建立对应的BaseStatementHandler，即SimpleStatementHandler、
        //PreparedStatementHandler或CallableStatementHandler，在RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。
        //我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截StatementHandler接口的prepare方法，又因为Mybatis只有在建立RoutingStatementHandler的时候
        //是通过Interceptor的plugin方法进行包裹的，所以我们这里拦截到的目标对象肯定是RoutingStatementHandler对象。
        RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
        //通过反射获取到当前RoutingStatementHandler对象的delegate属性
        StatementHandler delegate = (StatementHandler)ReflectUtil.getFieldValue(handler, "delegate");
        //获取到当前StatementHandler的 boundSql，这里不管是调用handler.getBoundSql()还是直接调用delegate.getBoundSql()结果是一样的，因为之前已经说过了
        //RoutingStatementHandler实现的所有StatementHandler接口方法里面都是调用的delegate对应的方法。
        BoundSql boundSql = delegate.getBoundSql();
        //拿到当前绑定Sql的参数对象，就是我们在调用对应的Mapper映射语句时所传入的参数对象
        Object obj = boundSql.getParameterObject();
        //这里我们简单的通过传入的是Page对象就认定它是需要进行分页操作的。
        if (obj instanceof Page<?>) {
            Page<?> page = (Page<?>) obj;
            //通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
            MappedStatement mappedStatement = (MappedStatement)ReflectUtil.getFieldValue(delegate, "mappedStatement");
            //拦截到的prepare方法参数是一个Connection对象
            Connection connection = (Connection)invocation.getArgs()[0];
            //获取当前要执行的Sql语句，也就是我们直接在Mapper映射语句中写的Sql语句
            String sql = boundSql.getSql();
            //给当前的page参数对象设置总记录数
            this.setTotalRecord(page,
                   mappedStatement, connection);
            //获取分页Sql语句
            String pageSql = this.getPageSql(page, sql);
            //利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
            ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
        }
        return invocation.proceed();
     */
}

